1 Introduction

City arborists in Burlington, Vermont have maintained a comprehensive dataset containing various features detailing every tree planted by the city since 1982, including date planted, GPS coordinates, land use type, species, diameter, height, tree condition, and appraisal estimate. These data have important implications in environmental assessments and future sustainability efforts from the city. The creation and curation of the data is part of the Urban Forestry Program by the Department of Parks and Recreation in Burlington, dedicated to expanding and protecting urban forests in the city. Trees, particularly in urban environments, can have massive impacts on the ecosystem. These benefits include increased property value, less drastic temperature fluctuations, reduced erosion, and air pollution control. According to the Burlington Parks and Rec Department, ” tree with a 50-year life span provides nearly $60,000 of benefit over its lifetime,” not to mention all the other un-quantifiable ways trees add to our lives through recreation, aesthetics, and ecosystem benefits (Burlington Parks and Recreation, 2022).

However, the current state of the data leaves a lot to be desired by those interested in analyzing it. There doesn’t appear to be any clear bias, but the dates included in the modified column expose a lack of updating to the dataset. The dataset was accessed in early 2022, and the most recent update listed is from December, 2019. Additionally, although the dataset is quite large, it is not comprehensive, and contains vast numbers of empty values or zeros. For example, the planted column, meant to inform the user of when the tree was planted, is about 85% zeros.

When assessing what data cleaning steps needed to be taken, we had to first decide whether the large number of zeros were due to a lack of value or a lack of information. Looking closer into the data, the variables containing zeros (diameter, height, spread, trunks, condiition, appraise, planted) could not realistically be zero and be a living tree. Therefore, we determined that all of the zeros were, in reality, null values and were adjusted accordingly to prevent zeros from skewing downstream analyses. In addition to zeros, we also converted any empty cells to NA as well. Other data cleaning steps, all outlined in our code below, included splitting Geo.Point, the coordinates, into longitude and latitude to increase ease of use. We also split up the species column into Genus and Species, to allow for better grouping for visualizations (though it should be noted that all genera and species are listed in common names, not scientific). The column also posed another problem: a lack of a common naming format. FOr example, two Maple species were named “mapl,red” and “mapl,sugar” but another one was named “maple free aut bl”, meaning that some fine tuning of the spelling and separation of the species names was necessary. We also updated the date format for the modified column.

The observational data curated by the city arborists is first and foremost a reference database for the city to keep track of the number and condition of trees in their care. For this to be a valuable asset, however, it requires that the city maintain and keep the information up-to-date. On the other hand, it can also be used by citizens to learn more about the trees on their street or in their neighborhood park. It provides anyone with a computer access to an abundance of information on the trees of Burlington and possesses a lot of potential to better understand the urban landscape of the city. This research project will analyze the Burlington tree data using visualizations and machine learning algorithms to better understand the abundance, distribution, and makeup of the urban forest in the city of Burlington, Vermont.

2 Visualization & Analysis

2.1 Data Cleaning

knitr::opts_chunk$set(echo = TRUE)
library(ggplot2)
Need help? Try Stackoverflow: https://stackoverflow.com/tags/ggplot2
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ─────────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✓ tibble  3.1.6     ✓ purrr   0.3.4
✓ tidyr   1.1.4     ✓ stringr 1.4.0
✓ readr   2.1.1     ✓ forcats 0.5.1
── Conflicts ────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
library(lubridate)

Attaching package: ‘lubridate’

The following objects are masked from ‘package:base’:

    date, intersect, setdiff, union
library(zoo)

Attaching package: ‘zoo’

The following objects are masked from ‘package:base’:

    as.Date, as.Date.numeric
library(mapview)
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
library(ggmap)
Google's Terms of Service: https://cloud.google.com/maps-platform/terms/.
Please cite ggmap if you use it! See citation("ggmap") for details.
theme_set(theme_bw())
# read in csv file
trees <- read.csv("./Burlington_Trees.csv")
library("magrittr")

# separate Geo.Point column to latitude and longitude
# and convert to numeric variables
trees <- trees %>% 
         separate(Geo.Point, c("lat", "long"), ",") %>% 
         mutate(lat = as.numeric(lat),
                long = as.numeric(long))

# separate species column into genus, species
trees <- trees %>% 
         separate(species, c("genus", "species"), ",")
Warning: Expected 2 pieces. Missing pieces filled with `NA` in 4170 rows [1, 4, 5, 7, 9, 13, 20, 21, 22, 23, 24, 25, 27, 28, 29, 30, 31, 32, 33, 34, ...].
# remove the "spp" included as a place holder for genus
trees$genus <- gsub(" spp", "", trees$genus)

# correct the misspelling of mapl to maple
trees$genus <- gsub("mapl", "maple", trees$genus, fixed = TRUE)
trees$genus <- gsub("maplee", "maple", trees$genus)

# convert zeros in numeric columns to NA so they will not be included in graphs
# in this case, zero values are due to lack of information, not lack of value,
# so all were converted to NA values to be filtered out later
# repeat for blank values
trees[trees == 0] <- NA
trees[trees == ""] <- NA

# convert dates to better format
trees <- trees %>% 
  mutate(modified = as.yearmon(modified, "%m/%Y"))

# cleaned data frame
head(trees)
NA

2.2 Number of Trees by Species

# number of trees by species
# histogram

spec_hist <- trees %>% group_by(genus) %>% mutate(freq = n()) %>% 
  filter(freq>100, genus!="NA", genus!="unknown") %>%
  ggplot(aes(x=reorder(genus, genus, function(x) length(x)))) +
  geom_bar(fill="forestgreen") + 
  coord_flip() +
  labs(title="Most common tree genuses in Burlington", x="Genus", y="Count") +
  scale_y_continuous(expand=c(0,0), limits=c(0,3000), breaks=seq(0,3000,500)) +
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(), panel.border= element_blank(),
        plot.title=element_text(hjust=0.5), axis.line = element_line(), plot.margin=unit(c(0.5,1,0.5,0.5),"cm"))
spec_hist

This bar chart shows the most common tree genuses in Burlington. Unsurprisingly to anyone who lives in Vermont, the maple tree is by far the most common tree in Burlington. Next comes the Ash, Crabapple, Linden, Oak, and Pine. Considering Burlington’s climate and elevation, these all make sense. In the higher elevations of Vermont, evergreens such as pine trees are more common, but since Burlington is by the lake, it has the deciduous trees that make it beautiful in the fall (and barren in the spring).

2.3 Relationship between species abundance and Land Use

# landuse v species
# multiple bar chart
# see if certain types of trees are more common by business, residential, etc.

# format the data into a new data fram
landuse_by_genus <- xtabs(formula = ~ landuse + genus,
                       data = trees) %>% 
  prop.table(margin = "landuse") %>%  
  data.frame() %>% 
  filter(Freq > .1) 
head(landuse_by_genus)
# make bar graph with frequency table
landuse_species_bar <- ggplot(data = landuse_by_genus,
       mapping = aes(x = landuse,
                     fill = genus,
                     y = Freq)) + 
  geom_bar(color = "black",
           stat = "identity",
           position = "fill") + 
  labs(y = "Proportion", 
       title = "Top Genus per Land Use Type",
       x = "Land Use Type") +
  theme(axis.text.x = element_text(angle = 30,
                                 hjust = 1))
landuse_species_bar

2.4 The Trees of Burlington

# will
# map of burlington with points as trees and colored by land use type, collated out by ward

trees_map_1 <- trees %>%
  filter(zone_id == 'ward 1')
  mapView(trees_map_1, xcol = "long", ycol = "lat", zcol = "landuse", crs = 4269, grid = FALSE)

trees_map_2 <- trees %>%
  filter(zone_id == 'ward 2')
  mapView(trees_map_2, xcol = "long", ycol = "lat", zcol = "landuse", crs = 4269, grid = FALSE)

trees_map_3 <- trees %>%
  filter(zone_id == 'ward 3')
  mapView(trees_map_3, xcol = "long", ycol = "lat", zcol = "landuse", crs = 4269, grid = FALSE)

trees_map_4 <- trees %>%
  filter(zone_id == 'ward 4')
  mapView(trees_map_4, xcol = "long", ycol = "lat", zcol = "landuse", crs = 4269, grid = FALSE)

trees_map_5 <- trees %>%
  filter(zone_id == 'ward 5')
  mapView(trees_map_5, xcol = "long", ycol = "lat", zcol = "landuse", crs = 4269, grid = FALSE)
  
trees_map_6 <- trees %>%
  filter(zone_id == 'ward 6')
  mapView(trees_map_6, xcol = "long", ycol = "lat", zcol = "landuse", crs = 4269, grid = FALSE)
  
trees_map_7 <- trees %>%
  filter(zone_id == 'ward 7')
  mapView(trees_map_7, xcol = "long", ycol = "lat", zcol = "landuse", crs = 4269, grid = FALSE)
  
trees_map_8 <- trees %>%
  filter(zone_id == 'ward 8')
  mapView(trees_map_8, xcol = "long", ycol = "lat", zcol = "landuse", crs = 4269, grid = FALSE)

trees_map_1
trees_map_2
trees_map_3
trees_map_4
trees_map_5
trees_map_6
trees_map_7
trees_map_8

# map of burlington with points as trees and colored by species (coming soon)

The maps above show the location of all trees in Burlington, collated into 8 separate maps sorted by ward number. On each map, we see the trees organized by their land use type (ie. residental vs commercial depending on the zone of that given plot of land). Based off of what I know about Burlington having lived here, this feels like it aligns well with how I think about various parts of the city. It is interesting to lay these points out on a map and see how they line up to various locations; it is clear to us that based on these maps, this dataset is incomplete in a lot of places. Specifically, ward 1 data seems to be missing trees from UVM, and some other spots in and around campus. This begs the question of how the data is collected and taged to produce the dataset we are now working with. However, in the end, this is certainly a unique way to look at zoning levels throughout the city through the various trees that are in each respective region.

QUESTION: I’m not quite sure the best way to arrange these maps; we elected to use an external mapping package instead of something like ggplot because it didn’t have data for the city of Burlington. Would love to see if you have any suggestions for changing zoom / making this more readable, or perhaps just an alternative mapping method. Thanks!

2.5 Appraisal Estimates

# diameter v appraisal
# scatterplot probably

appraisal <- trees %>% filter(appraise != "NA", diameter != "NA", height != "NA") %>%
  ggplot(mapping=aes(x=diameter,y=height, color=appraise)) +
  geom_point() +
  scale_color_gradient2(low = 'brown', high = 'green', midpoint=35000, mid="forestgreen") +
  labs(title="Appraisal Value by Height and Diameter", x="Diameter", y="Height", color="Appraisal Value")
  
appraisal

This graph shows the relationship between a tree’s diameter, height, and appraisal value. It is clear that there is a relationship between diameter and height, and thicker trees are usually taller. From this plot, it is clear that diameter is more influential than height for a tree’s value. The trees with relatively high height and small diameter are worth significantly less than the shorter, thicker trees. This probably has to do with the way that trees age “outwards,” making their diameter a better indicator of their age than their height. It seems that there are very few extremely valuable trees, and many trees which are small and cheap.

3 Machine Learning (coming soon)

4 Conclusion

(waiting on this seciton until we have finalized analysis post draft feedback)

5 Limitations

As discussed above, some limitations in our data are mainly centered around the incompleteness of the set. For the map section, it was clear that not every tree in Burlington was represented, and therefore it may prove difficult. Additionally, as made apparent by the multitude of NA values, there are many trees that came in with little to no information (ie. missing diameter, appraisal, etc.). This was interesting seeing as the tree was recorded but just missing various characteristics. However, despite these limitations, it is clear that this dataset is incredibly insightful and could be used in future projects to propose new arbory projects for the city, as well as increase sustainability initiatives through planting trees in areas identified as lacking through this data.

LS0tCnRpdGxlOiAiQSBTdGF0aXN0aWNhbCBhbmQgVmlzdWFsIEFuYWx5c2lzIG9mIE11bmljaXBhbGx5LU1haW50YWluZWQgVHJlZXMgaW4gdGhlIENpdHkgb2YgQnVybGluZ3RvbiwgVlQiCmF1dGhvcjogIklzYWJlbGxlIEZyYW5rZSwgQ2Fyb2xpbmUgR3JlZW4sIFdpbGwgR3Vpc2JvbmQiCmRhdGU6ICI0LzE1LzIwMjIiCm91dHB1dDoKICBwZGZfZG9jdW1lbnQ6CiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogIGh0bWxfbm90ZWJvb2s6CiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwotLS0KIyBJbnRyb2R1Y3Rpb24KCkNpdHkgYXJib3Jpc3RzIGluIEJ1cmxpbmd0b24sIFZlcm1vbnQgaGF2ZSBtYWludGFpbmVkIGEgY29tcHJlaGVuc2l2ZSBkYXRhc2V0IGNvbnRhaW5pbmcgdmFyaW91cyBmZWF0dXJlcyBkZXRhaWxpbmcgZXZlcnkgdHJlZSBwbGFudGVkIGJ5IHRoZSBjaXR5IHNpbmNlIDE5ODIsIGluY2x1ZGluZyBkYXRlIHBsYW50ZWQsIEdQUyBjb29yZGluYXRlcywgbGFuZCB1c2UgdHlwZSwgc3BlY2llcywgZGlhbWV0ZXIsIGhlaWdodCwgdHJlZSBjb25kaXRpb24sIGFuZCBhcHByYWlzYWwgZXN0aW1hdGUuIFRoZXNlIGRhdGEgaGF2ZSBpbXBvcnRhbnQgaW1wbGljYXRpb25zIGluIGVudmlyb25tZW50YWwgYXNzZXNzbWVudHMgYW5kIGZ1dHVyZSBzdXN0YWluYWJpbGl0eSBlZmZvcnRzIGZyb20gdGhlIGNpdHkuIFRoZSBjcmVhdGlvbiBhbmQgY3VyYXRpb24gb2YgdGhlIGRhdGEgaXMgcGFydCBvZiB0aGUgVXJiYW4gRm9yZXN0cnkgUHJvZ3JhbSBieSB0aGUgRGVwYXJ0bWVudCBvZiBQYXJrcyBhbmQgUmVjcmVhdGlvbiBpbiBCdXJsaW5ndG9uLCBkZWRpY2F0ZWQgdG8gZXhwYW5kaW5nIGFuZCBwcm90ZWN0aW5nIHVyYmFuIGZvcmVzdHMgaW4gdGhlIGNpdHkuIFRyZWVzLCBwYXJ0aWN1bGFybHkgaW4gdXJiYW4gZW52aXJvbm1lbnRzLCBjYW4gaGF2ZSBtYXNzaXZlIGltcGFjdHMgb24gdGhlIGVjb3N5c3RlbS4gVGhlc2UgYmVuZWZpdHMgaW5jbHVkZSBpbmNyZWFzZWQgcHJvcGVydHkgdmFsdWUsIGxlc3MgZHJhc3RpYyB0ZW1wZXJhdHVyZSBmbHVjdHVhdGlvbnMsIHJlZHVjZWQgZXJvc2lvbiwgYW5kIGFpciBwb2xsdXRpb24gY29udHJvbC4gQWNjb3JkaW5nIHRvIHRoZSBCdXJsaW5ndG9uIFBhcmtzIGFuZCBSZWMgRGVwYXJ0bWVudCwgIiB0cmVlIHdpdGggYSA1MC15ZWFyIGxpZmUgc3BhbiBwcm92aWRlcyBuZWFybHkgJDYwLDAwMCBvZiBiZW5lZml0IG92ZXIgaXRzIGxpZmV0aW1lLCIgbm90IHRvIG1lbnRpb24gYWxsIHRoZSBvdGhlciB1bi1xdWFudGlmaWFibGUgd2F5cyB0cmVlcyBhZGQgdG8gb3VyIGxpdmVzIHRocm91Z2ggcmVjcmVhdGlvbiwgYWVzdGhldGljcywgYW5kIGVjb3N5c3RlbSBiZW5lZml0cyAoQnVybGluZ3RvbiBQYXJrcyBhbmQgUmVjcmVhdGlvbiwgMjAyMikuCgpIb3dldmVyLCB0aGUgY3VycmVudCBzdGF0ZSBvZiB0aGUgZGF0YSBsZWF2ZXMgYSBsb3QgdG8gYmUgZGVzaXJlZCBieSB0aG9zZSBpbnRlcmVzdGVkIGluIGFuYWx5emluZyBpdC4gVGhlcmUgZG9lc24ndCBhcHBlYXIgdG8gYmUgYW55IGNsZWFyIGJpYXMsIGJ1dCB0aGUgZGF0ZXMgaW5jbHVkZWQgaW4gdGhlIGBtb2RpZmllZGAgY29sdW1uIGV4cG9zZSBhIGxhY2sgb2YgdXBkYXRpbmcgdG8gdGhlIGRhdGFzZXQuIFRoZSBkYXRhc2V0IHdhcyBhY2Nlc3NlZCBpbiBlYXJseSAyMDIyLCBhbmQgdGhlIG1vc3QgcmVjZW50IHVwZGF0ZSBsaXN0ZWQgaXMgZnJvbSBEZWNlbWJlciwgMjAxOS4gQWRkaXRpb25hbGx5LCBhbHRob3VnaCB0aGUgZGF0YXNldCBpcyBxdWl0ZSBsYXJnZSwgaXQgaXMgbm90IGNvbXByZWhlbnNpdmUsIGFuZCBjb250YWlucyB2YXN0IG51bWJlcnMgb2YgZW1wdHkgdmFsdWVzIG9yIHplcm9zLiBGb3IgZXhhbXBsZSwgdGhlIGBwbGFudGVkYCBjb2x1bW4sIG1lYW50IHRvIGluZm9ybSB0aGUgdXNlciBvZiB3aGVuIHRoZSB0cmVlIHdhcyBwbGFudGVkLCBpcyBhYm91dCA4NSUgemVyb3MuIAoKV2hlbiBhc3Nlc3Npbmcgd2hhdCBkYXRhIGNsZWFuaW5nIHN0ZXBzIG5lZWRlZCB0byBiZSB0YWtlbiwgd2UgaGFkIHRvIGZpcnN0IGRlY2lkZSB3aGV0aGVyIHRoZSBsYXJnZSBudW1iZXIgb2YgemVyb3Mgd2VyZSBkdWUgdG8gYSBsYWNrIG9mIHZhbHVlIG9yIGEgbGFjayBvZiBpbmZvcm1hdGlvbi4gTG9va2luZyBjbG9zZXIgaW50byB0aGUgZGF0YSwgdGhlIHZhcmlhYmxlcyBjb250YWluaW5nIHplcm9zIChgZGlhbWV0ZXJgLCBgaGVpZ2h0YCwgYHNwcmVhZGAsIGB0cnVua3NgLCBgY29uZGlpdGlvbmAsIGBhcHByYWlzZWAsIGBwbGFudGVkYCkgY291bGQgbm90IHJlYWxpc3RpY2FsbHkgYmUgemVybyBhbmQgYmUgYSBsaXZpbmcgdHJlZS4gVGhlcmVmb3JlLCB3ZSBkZXRlcm1pbmVkIHRoYXQgYWxsIG9mIHRoZSB6ZXJvcyB3ZXJlLCBpbiByZWFsaXR5LCBudWxsIHZhbHVlcyBhbmQgd2VyZSBhZGp1c3RlZCBhY2NvcmRpbmdseSB0byBwcmV2ZW50IHplcm9zIGZyb20gc2tld2luZyBkb3duc3RyZWFtIGFuYWx5c2VzLiBJbiBhZGRpdGlvbiB0byB6ZXJvcywgd2UgYWxzbyBjb252ZXJ0ZWQgYW55IGVtcHR5IGNlbGxzIHRvIE5BIGFzIHdlbGwuIE90aGVyIGRhdGEgY2xlYW5pbmcgc3RlcHMsIGFsbCBvdXRsaW5lZCBpbiBvdXIgY29kZSBiZWxvdywgaW5jbHVkZWQgc3BsaXR0aW5nIGBHZW8uUG9pbnRgLCB0aGUgY29vcmRpbmF0ZXMsIGludG8gbG9uZ2l0dWRlIGFuZCBsYXRpdHVkZSB0byBpbmNyZWFzZSBlYXNlIG9mIHVzZS4gV2UgYWxzbyBzcGxpdCB1cCB0aGUgYHNwZWNpZXNgIGNvbHVtbiBpbnRvIEdlbnVzIGFuZCBTcGVjaWVzLCB0byBhbGxvdyBmb3IgYmV0dGVyIGdyb3VwaW5nIGZvciB2aXN1YWxpemF0aW9ucyAodGhvdWdoIGl0IHNob3VsZCBiZSBub3RlZCB0aGF0IGFsbCBnZW5lcmEgYW5kIHNwZWNpZXMgYXJlIGxpc3RlZCBpbiBjb21tb24gbmFtZXMsIG5vdCBzY2llbnRpZmljKS4gVGhlIGNvbHVtbiBhbHNvIHBvc2VkIGFub3RoZXIgcHJvYmxlbTogYSBsYWNrIG9mIGEgY29tbW9uIG5hbWluZyBmb3JtYXQuIEZPciBleGFtcGxlLCB0d28gTWFwbGUgc3BlY2llcyB3ZXJlIG5hbWVkICJtYXBsLHJlZCIgYW5kICJtYXBsLHN1Z2FyIiBidXQgYW5vdGhlciBvbmUgd2FzIG5hbWVkICJtYXBsZSBmcmVlIGF1dCBibCIsIG1lYW5pbmcgdGhhdCBzb21lIGZpbmUgdHVuaW5nIG9mIHRoZSBzcGVsbGluZyBhbmQgc2VwYXJhdGlvbiBvZiB0aGUgc3BlY2llcyBuYW1lcyB3YXMgbmVjZXNzYXJ5LiBXZSBhbHNvIHVwZGF0ZWQgdGhlIGRhdGUgZm9ybWF0IGZvciB0aGUgYG1vZGlmaWVkYCBjb2x1bW4uIAoKVGhlIG9ic2VydmF0aW9uYWwgZGF0YSBjdXJhdGVkIGJ5IHRoZSBjaXR5IGFyYm9yaXN0cyBpcyBmaXJzdCBhbmQgZm9yZW1vc3QgYSByZWZlcmVuY2UgZGF0YWJhc2UgZm9yIHRoZSBjaXR5IHRvIGtlZXAgdHJhY2sgb2YgdGhlIG51bWJlciBhbmQgY29uZGl0aW9uIG9mIHRyZWVzIGluIHRoZWlyIGNhcmUuIEZvciB0aGlzIHRvIGJlIGEgdmFsdWFibGUgYXNzZXQsIGhvd2V2ZXIsIGl0IHJlcXVpcmVzIHRoYXQgdGhlIGNpdHkgbWFpbnRhaW4gYW5kIGtlZXAgdGhlIGluZm9ybWF0aW9uIHVwLXRvLWRhdGUuIE9uIHRoZSBvdGhlciBoYW5kLCBpdCBjYW4gYWxzbyBiZSB1c2VkIGJ5IGNpdGl6ZW5zIHRvIGxlYXJuIG1vcmUgYWJvdXQgdGhlIHRyZWVzIG9uIHRoZWlyIHN0cmVldCBvciBpbiB0aGVpciBuZWlnaGJvcmhvb2QgcGFyay4gSXQgcHJvdmlkZXMgYW55b25lIHdpdGggYSBjb21wdXRlciBhY2Nlc3MgdG8gYW4gYWJ1bmRhbmNlIG9mIGluZm9ybWF0aW9uIG9uIHRoZSB0cmVlcyBvZiBCdXJsaW5ndG9uIGFuZCBwb3NzZXNzZXMgYSBsb3Qgb2YgcG90ZW50aWFsIHRvIGJldHRlciB1bmRlcnN0YW5kIHRoZSB1cmJhbiBsYW5kc2NhcGUgb2YgdGhlIGNpdHkuIFRoaXMgcmVzZWFyY2ggcHJvamVjdCB3aWxsIGFuYWx5emUgdGhlIEJ1cmxpbmd0b24gdHJlZSBkYXRhIHVzaW5nIHZpc3VhbGl6YXRpb25zIGFuZCBtYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobXMgdG8gYmV0dGVyIHVuZGVyc3RhbmQgdGhlIGFidW5kYW5jZSwgZGlzdHJpYnV0aW9uLCBhbmQgbWFrZXVwIG9mIHRoZSB1cmJhbiBmb3Jlc3QgaW4gdGhlIGNpdHkgb2YgQnVybGluZ3RvbiwgVmVybW9udC4KCgojIFZpc3VhbGl6YXRpb24gJiBBbmFseXNpcwoKIyMgRGF0YSBDbGVhbmluZwpgYGB7ciBzZXR1cH0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeSh6b28pCmxpYnJhcnkobWFwdmlldykKbGlicmFyeShnZ21hcCkKbGlicmFyeShncmlkKQoKdGhlbWVfc2V0KHRoZW1lX2J3KCkpCgpgYGAKCmBgYHtyIGRhdGFfY2xlYW59CiMgcmVhZCBpbiBjc3YgZmlsZQp0cmVlcyA8LSByZWFkLmNzdigiLi9CdXJsaW5ndG9uX1RyZWVzLmNzdiIpCmxpYnJhcnkoIm1hZ3JpdHRyIikKCiMgc2VwYXJhdGUgR2VvLlBvaW50IGNvbHVtbiB0byBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlCiMgYW5kIGNvbnZlcnQgdG8gbnVtZXJpYyB2YXJpYWJsZXMKdHJlZXMgPC0gdHJlZXMgJT4lIAogICAgICAgICBzZXBhcmF0ZShHZW8uUG9pbnQsIGMoImxhdCIsICJsb25nIiksICIsIikgJT4lIAogICAgICAgICBtdXRhdGUobGF0ID0gYXMubnVtZXJpYyhsYXQpLAogICAgICAgICAgICAgICAgbG9uZyA9IGFzLm51bWVyaWMobG9uZykpCgojIHNlcGFyYXRlIHNwZWNpZXMgY29sdW1uIGludG8gZ2VudXMsIHNwZWNpZXMKdHJlZXMgPC0gdHJlZXMgJT4lIAogICAgICAgICBzZXBhcmF0ZShzcGVjaWVzLCBjKCJnZW51cyIsICJzcGVjaWVzIiksICIsIikKCiMgcmVtb3ZlIHRoZSAic3BwIiBpbmNsdWRlZCBhcyBhIHBsYWNlIGhvbGRlciBmb3IgZ2VudXMKdHJlZXMkZ2VudXMgPC0gZ3N1YigiIHNwcCIsICIiLCB0cmVlcyRnZW51cykKCiMgY29ycmVjdCB0aGUgbWlzc3BlbGxpbmcgb2YgbWFwbCB0byBtYXBsZQp0cmVlcyRnZW51cyA8LSBnc3ViKCJtYXBsIiwgIm1hcGxlIiwgdHJlZXMkZ2VudXMsIGZpeGVkID0gVFJVRSkKdHJlZXMkZ2VudXMgPC0gZ3N1YigibWFwbGVlIiwgIm1hcGxlIiwgdHJlZXMkZ2VudXMpCgojIGNvbnZlcnQgemVyb3MgaW4gbnVtZXJpYyBjb2x1bW5zIHRvIE5BIHNvIHRoZXkgd2lsbCBub3QgYmUgaW5jbHVkZWQgaW4gZ3JhcGhzCiMgaW4gdGhpcyBjYXNlLCB6ZXJvIHZhbHVlcyBhcmUgZHVlIHRvIGxhY2sgb2YgaW5mb3JtYXRpb24sIG5vdCBsYWNrIG9mIHZhbHVlLAojIHNvIGFsbCB3ZXJlIGNvbnZlcnRlZCB0byBOQSB2YWx1ZXMgdG8gYmUgZmlsdGVyZWQgb3V0IGxhdGVyCiMgcmVwZWF0IGZvciBibGFuayB2YWx1ZXMKdHJlZXNbdHJlZXMgPT0gMF0gPC0gTkEKdHJlZXNbdHJlZXMgPT0gIiJdIDwtIE5BCgojIGNvbnZlcnQgZGF0ZXMgdG8gYmV0dGVyIGZvcm1hdAp0cmVlcyA8LSB0cmVlcyAlPiUgCiAgbXV0YXRlKG1vZGlmaWVkID0gYXMueWVhcm1vbihtb2RpZmllZCwgIiVtLyVZIikpCgojIGNsZWFuZWQgZGF0YSBmcmFtZQpoZWFkKHRyZWVzKQoKYGBgCgojIyBOdW1iZXIgb2YgVHJlZXMgYnkgU3BlY2llcwoKYGBge3J9CiMgbnVtYmVyIG9mIHRyZWVzIGJ5IHNwZWNpZXMKIyBoaXN0b2dyYW0KCnNwZWNfaGlzdCA8LSB0cmVlcyAlPiUgZ3JvdXBfYnkoZ2VudXMpICU+JSBtdXRhdGUoZnJlcSA9IG4oKSkgJT4lIAogIGZpbHRlcihmcmVxPjEwMCwgZ2VudXMhPSJOQSIsIGdlbnVzIT0idW5rbm93biIpICU+JQogIGdncGxvdChhZXMoeD1yZW9yZGVyKGdlbnVzLCBnZW51cywgZnVuY3Rpb24oeCkgbGVuZ3RoKHgpKSkpICsKICBnZW9tX2JhcihmaWxsPSJmb3Jlc3RncmVlbiIpICsgCiAgY29vcmRfZmxpcCgpICsKICBsYWJzKHRpdGxlPSJNb3N0IGNvbW1vbiB0cmVlIGdlbnVzZXMgaW4gQnVybGluZ3RvbiIsIHg9IkdlbnVzIiwgeT0iQ291bnQiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZD1jKDAsMCksIGxpbWl0cz1jKDAsMzAwMCksIGJyZWFrcz1zZXEoMCwzMDAwLDUwMCkpICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ib3JkZXI9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChoanVzdD0wLjUpLCBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoKSwgcGxvdC5tYXJnaW49dW5pdChjKDAuNSwxLDAuNSwwLjUpLCJjbSIpKQpzcGVjX2hpc3QKCmBgYAoKVGhpcyBiYXIgY2hhcnQgc2hvd3MgdGhlIG1vc3QgY29tbW9uIHRyZWUgZ2VudXNlcyBpbiBCdXJsaW5ndG9uLiBVbnN1cnByaXNpbmdseSB0byBhbnlvbmUgd2hvIGxpdmVzIGluIFZlcm1vbnQsIHRoZSBtYXBsZSB0cmVlIGlzIGJ5IGZhciB0aGUgbW9zdCBjb21tb24gdHJlZSBpbiBCdXJsaW5ndG9uLiBOZXh0IGNvbWVzIHRoZSBBc2gsIENyYWJhcHBsZSwgTGluZGVuLCBPYWssIGFuZCBQaW5lLiBDb25zaWRlcmluZyBCdXJsaW5ndG9uJ3MgY2xpbWF0ZSBhbmQgZWxldmF0aW9uLCB0aGVzZSBhbGwgbWFrZSBzZW5zZS4gSW4gdGhlIGhpZ2hlciBlbGV2YXRpb25zIG9mIFZlcm1vbnQsIGV2ZXJncmVlbnMgc3VjaCBhcyBwaW5lIHRyZWVzIGFyZSBtb3JlIGNvbW1vbiwgYnV0IHNpbmNlIEJ1cmxpbmd0b24gaXMgYnkgdGhlIGxha2UsIGl0IGhhcyB0aGUgZGVjaWR1b3VzIHRyZWVzIHRoYXQgbWFrZSBpdCBiZWF1dGlmdWwgaW4gdGhlIGZhbGwgKGFuZCBiYXJyZW4gaW4gdGhlIHNwcmluZykuCgojIyBSZWxhdGlvbnNoaXAgYmV0d2VlbiBzcGVjaWVzIGFidW5kYW5jZSBhbmQgTGFuZCBVc2UKCmBgYHtyfQojIGxhbmR1c2UgdiBzcGVjaWVzCiMgbXVsdGlwbGUgYmFyIGNoYXJ0CiMgc2VlIGlmIGNlcnRhaW4gdHlwZXMgb2YgdHJlZXMgYXJlIG1vcmUgY29tbW9uIGJ5IGJ1c2luZXNzLCByZXNpZGVudGlhbCwgZXRjLgoKIyBmb3JtYXQgdGhlIGRhdGEgaW50byBhIG5ldyBkYXRhIGZyYW0KbGFuZHVzZV9ieV9nZW51cyA8LSB4dGFicyhmb3JtdWxhID0gfiBsYW5kdXNlICsgZ2VudXMsCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyZWVzKSAlPiUgCiAgcHJvcC50YWJsZShtYXJnaW4gPSAibGFuZHVzZSIpICU+JSAgCiAgZGF0YS5mcmFtZSgpICU+JSAKICBmaWx0ZXIoRnJlcSA+IC4xKSAKaGVhZChsYW5kdXNlX2J5X2dlbnVzKQojIG1ha2UgYmFyIGdyYXBoIHdpdGggZnJlcXVlbmN5IHRhYmxlCmxhbmR1c2Vfc3BlY2llc19iYXIgPC0gZ2dwbG90KGRhdGEgPSBsYW5kdXNlX2J5X2dlbnVzLAogICAgICAgbWFwcGluZyA9IGFlcyh4ID0gbGFuZHVzZSwKICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGdlbnVzLAogICAgICAgICAgICAgICAgICAgICB5ID0gRnJlcSkpICsgCiAgZ2VvbV9iYXIoY29sb3IgPSAiYmxhY2siLAogICAgICAgICAgIHN0YXQgPSAiaWRlbnRpdHkiLAogICAgICAgICAgIHBvc2l0aW9uID0gImZpbGwiKSArIAogIGxhYnMoeSA9ICJQcm9wb3J0aW9uIiwgCiAgICAgICB0aXRsZSA9ICJUb3AgR2VudXMgcGVyIExhbmQgVXNlIFR5cGUiLAogICAgICAgeCA9ICJMYW5kIFVzZSBUeXBlIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMzAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMSkpCmxhbmR1c2Vfc3BlY2llc19iYXIKCmBgYAoKIyMgVGhlIFRyZWVzIG9mIEJ1cmxpbmd0b24KCmBgYHtyIGxhbmRfdXNlX2Rpc3RyaWJ9CiMgd2lsbAojIG1hcCBvZiBidXJsaW5ndG9uIHdpdGggcG9pbnRzIGFzIHRyZWVzIGFuZCBjb2xvcmVkIGJ5IGxhbmQgdXNlIHR5cGUsIGNvbGxhdGVkIG91dCBieSB3YXJkCgp0cmVlc19tYXBfMSA8LSB0cmVlcyAlPiUKICBmaWx0ZXIoem9uZV9pZCA9PSAnd2FyZCAxJykKICBtYXBWaWV3KHRyZWVzX21hcF8xLCB4Y29sID0gImxvbmciLCB5Y29sID0gImxhdCIsIHpjb2wgPSAibGFuZHVzZSIsIGNycyA9IDQyNjksIGdyaWQgPSBGQUxTRSkKCnRyZWVzX21hcF8yIDwtIHRyZWVzICU+JQogIGZpbHRlcih6b25lX2lkID09ICd3YXJkIDInKQogIG1hcFZpZXcodHJlZXNfbWFwXzIsIHhjb2wgPSAibG9uZyIsIHljb2wgPSAibGF0IiwgemNvbCA9ICJsYW5kdXNlIiwgY3JzID0gNDI2OSwgZ3JpZCA9IEZBTFNFKQoKdHJlZXNfbWFwXzMgPC0gdHJlZXMgJT4lCiAgZmlsdGVyKHpvbmVfaWQgPT0gJ3dhcmQgMycpCiAgbWFwVmlldyh0cmVlc19tYXBfMywgeGNvbCA9ICJsb25nIiwgeWNvbCA9ICJsYXQiLCB6Y29sID0gImxhbmR1c2UiLCBjcnMgPSA0MjY5LCBncmlkID0gRkFMU0UpCgp0cmVlc19tYXBfNCA8LSB0cmVlcyAlPiUKICBmaWx0ZXIoem9uZV9pZCA9PSAnd2FyZCA0JykKICBtYXBWaWV3KHRyZWVzX21hcF80LCB4Y29sID0gImxvbmciLCB5Y29sID0gImxhdCIsIHpjb2wgPSAibGFuZHVzZSIsIGNycyA9IDQyNjksIGdyaWQgPSBGQUxTRSkKCnRyZWVzX21hcF81IDwtIHRyZWVzICU+JQogIGZpbHRlcih6b25lX2lkID09ICd3YXJkIDUnKQogIG1hcFZpZXcodHJlZXNfbWFwXzUsIHhjb2wgPSAibG9uZyIsIHljb2wgPSAibGF0IiwgemNvbCA9ICJsYW5kdXNlIiwgY3JzID0gNDI2OSwgZ3JpZCA9IEZBTFNFKQogIAp0cmVlc19tYXBfNiA8LSB0cmVlcyAlPiUKICBmaWx0ZXIoem9uZV9pZCA9PSAnd2FyZCA2JykKICBtYXBWaWV3KHRyZWVzX21hcF82LCB4Y29sID0gImxvbmciLCB5Y29sID0gImxhdCIsIHpjb2wgPSAibGFuZHVzZSIsIGNycyA9IDQyNjksIGdyaWQgPSBGQUxTRSkKICAKdHJlZXNfbWFwXzcgPC0gdHJlZXMgJT4lCiAgZmlsdGVyKHpvbmVfaWQgPT0gJ3dhcmQgNycpCiAgbWFwVmlldyh0cmVlc19tYXBfNywgeGNvbCA9ICJsb25nIiwgeWNvbCA9ICJsYXQiLCB6Y29sID0gImxhbmR1c2UiLCBjcnMgPSA0MjY5LCBncmlkID0gRkFMU0UpCiAgCnRyZWVzX21hcF84IDwtIHRyZWVzICU+JQogIGZpbHRlcih6b25lX2lkID09ICd3YXJkIDgnKQogIG1hcFZpZXcodHJlZXNfbWFwXzgsIHhjb2wgPSAibG9uZyIsIHljb2wgPSAibGF0IiwgemNvbCA9ICJsYW5kdXNlIiwgY3JzID0gNDI2OSwgZ3JpZCA9IEZBTFNFKQoKdHJlZXNfbWFwXzEKdHJlZXNfbWFwXzIKdHJlZXNfbWFwXzMKdHJlZXNfbWFwXzQKdHJlZXNfbWFwXzUKdHJlZXNfbWFwXzYKdHJlZXNfbWFwXzcKdHJlZXNfbWFwXzgKCiMgbWFwIG9mIGJ1cmxpbmd0b24gd2l0aCBwb2ludHMgYXMgdHJlZXMgYW5kIGNvbG9yZWQgYnkgc3BlY2llcyAoY29taW5nIHNvb24pCgpgYGAKClRoZSBtYXBzIGFib3ZlIHNob3cgdGhlIGxvY2F0aW9uIG9mIGFsbCB0cmVlcyBpbiBCdXJsaW5ndG9uLCBjb2xsYXRlZCBpbnRvIDggc2VwYXJhdGUgbWFwcyBzb3J0ZWQgYnkgd2FyZCBudW1iZXIuIE9uIGVhY2ggbWFwLCB3ZSBzZWUgdGhlIHRyZWVzIG9yZ2FuaXplZCBieSB0aGVpciBsYW5kIHVzZSB0eXBlIChpZS4gcmVzaWRlbnRhbCB2cyBjb21tZXJjaWFsIGRlcGVuZGluZyBvbiB0aGUgem9uZSBvZiB0aGF0IGdpdmVuIHBsb3Qgb2YgbGFuZCkuIEJhc2VkIG9mZiBvZiB3aGF0IEkga25vdyBhYm91dCBCdXJsaW5ndG9uIGhhdmluZyBsaXZlZCBoZXJlLCB0aGlzIGZlZWxzIGxpa2UgaXQgYWxpZ25zIHdlbGwgd2l0aCBob3cgSSB0aGluayBhYm91dCB2YXJpb3VzIHBhcnRzIG9mIHRoZSBjaXR5LiBJdCBpcyBpbnRlcmVzdGluZyB0byBsYXkgdGhlc2UgcG9pbnRzIG91dCBvbiBhIG1hcCBhbmQgc2VlIGhvdyB0aGV5IGxpbmUgdXAgdG8gdmFyaW91cyBsb2NhdGlvbnM7IGl0IGlzIGNsZWFyIHRvIHVzIHRoYXQgYmFzZWQgb24gdGhlc2UgbWFwcywgdGhpcyBkYXRhc2V0IGlzIGluY29tcGxldGUgaW4gYSBsb3Qgb2YgcGxhY2VzLiBTcGVjaWZpY2FsbHksIHdhcmQgMSBkYXRhIHNlZW1zIHRvIGJlIG1pc3NpbmcgdHJlZXMgZnJvbSBVVk0sIGFuZCBzb21lIG90aGVyIHNwb3RzIGluIGFuZCBhcm91bmQgY2FtcHVzLiBUaGlzIGJlZ3MgdGhlIHF1ZXN0aW9uIG9mIGhvdyB0aGUgZGF0YSBpcyBjb2xsZWN0ZWQgYW5kIHRhZ2VkIHRvIHByb2R1Y2UgdGhlIGRhdGFzZXQgd2UgYXJlIG5vdyB3b3JraW5nIHdpdGguIEhvd2V2ZXIsIGluIHRoZSBlbmQsIHRoaXMgaXMgY2VydGFpbmx5IGEgdW5pcXVlIHdheSB0byBsb29rIGF0IHpvbmluZyBsZXZlbHMgdGhyb3VnaG91dCB0aGUgY2l0eSB0aHJvdWdoIHRoZSB2YXJpb3VzIHRyZWVzIHRoYXQgYXJlIGluIGVhY2ggcmVzcGVjdGl2ZSByZWdpb24uIAoKUVVFU1RJT046IEknbSBub3QgcXVpdGUgc3VyZSB0aGUgYmVzdCB3YXkgdG8gYXJyYW5nZSB0aGVzZSBtYXBzOyB3ZSBlbGVjdGVkIHRvIHVzZSBhbiBleHRlcm5hbCBtYXBwaW5nIHBhY2thZ2UgaW5zdGVhZCBvZiBzb21ldGhpbmcgbGlrZSBnZ3Bsb3QgYmVjYXVzZSBpdCBkaWRuJ3QgaGF2ZSBkYXRhIGZvciB0aGUgY2l0eSBvZiBCdXJsaW5ndG9uLiBXb3VsZCBsb3ZlIHRvIHNlZSBpZiB5b3UgaGF2ZSBhbnkgc3VnZ2VzdGlvbnMgZm9yIGNoYW5naW5nIHpvb20gLyBtYWtpbmcgdGhpcyBtb3JlIHJlYWRhYmxlLCBvciBwZXJoYXBzIGp1c3QgYW4gYWx0ZXJuYXRpdmUgbWFwcGluZyBtZXRob2QuIFRoYW5rcyEKCiMjIEFwcHJhaXNhbCBFc3RpbWF0ZXMKCmBgYHtyfQojIGRpYW1ldGVyIHYgYXBwcmFpc2FsCiMgc2NhdHRlcnBsb3QgcHJvYmFibHkKCmFwcHJhaXNhbCA8LSB0cmVlcyAlPiUgZmlsdGVyKGFwcHJhaXNlICE9ICJOQSIsIGRpYW1ldGVyICE9ICJOQSIsIGhlaWdodCAhPSAiTkEiKSAlPiUKICBnZ3Bsb3QobWFwcGluZz1hZXMoeD1kaWFtZXRlcix5PWhlaWdodCwgY29sb3I9YXBwcmFpc2UpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudDIobG93ID0gJ2Jyb3duJywgaGlnaCA9ICdncmVlbicsIG1pZHBvaW50PTM1MDAwLCBtaWQ9ImZvcmVzdGdyZWVuIikgKwogIGxhYnModGl0bGU9IkFwcHJhaXNhbCBWYWx1ZSBieSBIZWlnaHQgYW5kIERpYW1ldGVyIiwgeD0iRGlhbWV0ZXIiLCB5PSJIZWlnaHQiLCBjb2xvcj0iQXBwcmFpc2FsIFZhbHVlIikKICAKYXBwcmFpc2FsCgpgYGAKVGhpcyBncmFwaCBzaG93cyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYSB0cmVlJ3MgZGlhbWV0ZXIsIGhlaWdodCwgYW5kIGFwcHJhaXNhbCB2YWx1ZS4gSXQgaXMgY2xlYXIgdGhhdCB0aGVyZSBpcyBhIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGRpYW1ldGVyIGFuZCBoZWlnaHQsIGFuZCB0aGlja2VyIHRyZWVzIGFyZSB1c3VhbGx5IHRhbGxlci4gRnJvbSB0aGlzIHBsb3QsIGl0IGlzIGNsZWFyIHRoYXQgZGlhbWV0ZXIgaXMgbW9yZSBpbmZsdWVudGlhbCB0aGFuIGhlaWdodCBmb3IgYSB0cmVlJ3MgdmFsdWUuIFRoZSB0cmVlcyB3aXRoIHJlbGF0aXZlbHkgaGlnaCBoZWlnaHQgYW5kIHNtYWxsIGRpYW1ldGVyIGFyZSB3b3J0aCBzaWduaWZpY2FudGx5IGxlc3MgdGhhbiB0aGUgc2hvcnRlciwgdGhpY2tlciB0cmVlcy4gVGhpcyBwcm9iYWJseSBoYXMgdG8gZG8gd2l0aCB0aGUgd2F5IHRoYXQgdHJlZXMgYWdlICJvdXR3YXJkcywiIG1ha2luZyB0aGVpciBkaWFtZXRlciBhIGJldHRlciBpbmRpY2F0b3Igb2YgdGhlaXIgYWdlIHRoYW4gdGhlaXIgaGVpZ2h0LiBJdCBzZWVtcyB0aGF0IHRoZXJlIGFyZSB2ZXJ5IGZldyBleHRyZW1lbHkgdmFsdWFibGUgdHJlZXMsIGFuZCBtYW55IHRyZWVzIHdoaWNoIGFyZSBzbWFsbCBhbmQgY2hlYXAuCgoKIyBNYWNoaW5lIExlYXJuaW5nIChjb21pbmcgc29vbikKCiMgQ29uY2x1c2lvbgoKKHdhaXRpbmcgb24gdGhpcyBzZWNpdG9uIHVudGlsIHdlIGhhdmUgZmluYWxpemVkIGFuYWx5c2lzIHBvc3QgZHJhZnQgZmVlZGJhY2spCgojIExpbWl0YXRpb25zCgpBcyBkaXNjdXNzZWQgYWJvdmUsIHNvbWUgbGltaXRhdGlvbnMgaW4gb3VyIGRhdGEgYXJlIG1haW5seSBjZW50ZXJlZCBhcm91bmQgdGhlIGluY29tcGxldGVuZXNzIG9mIHRoZSBzZXQuIEZvciB0aGUgbWFwIHNlY3Rpb24sIGl0IHdhcyBjbGVhciB0aGF0IG5vdCBldmVyeSB0cmVlIGluIEJ1cmxpbmd0b24gd2FzIHJlcHJlc2VudGVkLCBhbmQgdGhlcmVmb3JlIGl0IG1heSBwcm92ZSBkaWZmaWN1bHQuIEFkZGl0aW9uYWxseSwgYXMgbWFkZSBhcHBhcmVudCBieSB0aGUgbXVsdGl0dWRlIG9mIE5BIHZhbHVlcywgdGhlcmUgYXJlIG1hbnkgdHJlZXMgdGhhdCBjYW1lIGluIHdpdGggbGl0dGxlIHRvIG5vIGluZm9ybWF0aW9uIChpZS4gbWlzc2luZyBkaWFtZXRlciwgYXBwcmFpc2FsLCBldGMuKS4gVGhpcyB3YXMgaW50ZXJlc3Rpbmcgc2VlaW5nIGFzIHRoZSB0cmVlIHdhcyByZWNvcmRlZCBidXQganVzdCBtaXNzaW5nIHZhcmlvdXMgY2hhcmFjdGVyaXN0aWNzLiBIb3dldmVyLCBkZXNwaXRlIHRoZXNlIGxpbWl0YXRpb25zLCBpdCBpcyBjbGVhciB0aGF0IHRoaXMgZGF0YXNldCBpcyBpbmNyZWRpYmx5IGluc2lnaHRmdWwgYW5kIGNvdWxkIGJlIHVzZWQgaW4gZnV0dXJlIHByb2plY3RzIHRvIHByb3Bvc2UgbmV3IGFyYm9yeSBwcm9qZWN0cyBmb3IgdGhlIGNpdHksIGFzIHdlbGwgYXMgaW5jcmVhc2Ugc3VzdGFpbmFiaWxpdHkgaW5pdGlhdGl2ZXMgdGhyb3VnaCBwbGFudGluZyB0cmVlcyBpbiBhcmVhcyBpZGVudGlmaWVkIGFzIGxhY2tpbmcgdGhyb3VnaCB0aGlzIGRhdGEuIAoK